﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace Konves.Collections
{
    public sealed partial class IntervalDictionary<TBound, TValue>
    {
        [DebuggerDisplay("[{LowerBound},{UpperBound}]")]
        // TODO: Add XML Documentation
        private class Interval : IInterval<TBound>
        {
            public Interval(TBound lower, TBound upper)
            {
                if (object.ReferenceEquals(lower, null))
                    throw new ArgumentNullException("lower", "lower is null");
                if (object.ReferenceEquals(lower, null))
                    throw new ArgumentNullException("upper", "upper is null");

                this.LowerBound = lower;
                this.UpperBound = upper;
            }

            public TBound LowerBound { get; private set; }

            public TBound UpperBound { get; private set; }

            public bool Contains(TBound key)
            {
                return
                    this.LowerBound.CompareTo(key) <= 0
                    && key.CompareTo(this.UpperBound) <= 0;
            }

            public bool Intersects(IInterval<TBound> other)
            {
                return
                    this.Contains(other.LowerBound)
                    || other.Contains(this.LowerBound);
            }

            public bool IsSubsetOf(IInterval<TBound> other)
            {
                return
                    other.Contains(this.LowerBound)
                    && other.Contains(this.UpperBound);
            }

            public bool IsSupersetOf(IInterval<TBound> other)
            {
                return
                    this.Contains(other.LowerBound)
                    && this.Contains(other.UpperBound);
            }

            public IInterval<TBound> Intersect(IInterval<TBound> other)
            {
                if (!this.Intersects(other))
                    throw new ArgumentException("other", "other and this instance do not intersect.");

                return
                    new Interval/*<TBound>*/(
                        Min(this.LowerBound, other.LowerBound),
                        Max(this.UpperBound, other.UpperBound)
                        );
            }

            public IInterval<TBound> Union(IInterval<TBound> other)
            {
                if (!this.Intersects(other))
                    throw new ArgumentException("other", "other and this instance do not intersect.");

                return
                    new Interval/*<TBound>*/(
                        Max(this.LowerBound, other.LowerBound),
                        Min(this.UpperBound, other.UpperBound)
                        );
            }

            /// <summary>
            /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
            /// </summary>
            /// <param name="obj">An object to compare with this instance.</param>
            /// <returns>
            /// A value that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance is less than <paramref name="obj"/>. Zero This instance is equal to <paramref name="obj"/>. Greater than zero This instance is greater than <paramref name="obj"/>.
            /// </returns>
            /// <exception cref="T:System.ArgumentException">
            ///   <paramref name="obj"/> is not the same type as this instance. </exception>
            public int CompareTo(object obj)
            {
                if (!(obj is Interval/*<TBound>*/))
                    throw new ArgumentException("obj", "obj is not of same type as this instance.");
                else
                    return CompareTo(obj as Interval/*<TBound>*/);
            }

            /// <summary>
            /// Compares the current object with another object of the same type.
            /// </summary>
            /// <param name="other">An object to compare with this object.</param>
            /// <returns>
            /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following meanings:
            /// Value
            /// Meaning
            /// Less than zero
            /// This object is less than the <paramref name="other"/> parameter.
            /// Zero
            /// This object is equal to <paramref name="other"/>.
            /// Greater than zero
            /// This object is greater than <paramref name="other"/>.
            /// </returns>
            public int CompareTo(IInterval<TBound> other)
            {
                return this.LowerBound.CompareTo(other.LowerBound);
            }

            /// <summary>
            /// Indicates whether the current object is equal to another object of the same type.
            /// </summary>
            /// <param name="other">An object to compare with this object.</param>
            /// <returns>
            /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
            /// </returns>
            public bool Equals(IInterval<TBound> other)
            {
                return
                    this.LowerBound.Equals(other.LowerBound)
                    && this.UpperBound.Equals(other.UpperBound);
            }

            public static TBound Max(TBound a, TBound b)
            {
                return a.CompareTo(b) > 0 ? a : b;
            }

            public static TBound Min(TBound a, TBound b)
            {
                return a.CompareTo(b) < 0 ? a : b;
            }

            #region Explicitly Implemented Members
            bool IInterval<TBound>.IsLowerBoundInclusive
            {
                get { return true; }
            }

            bool IInterval<TBound>.IsUpperBoundInclusive
            {
                get { return true; }
            }

            IInterval<TBound> IInterval<TBound>.Subtract(IInterval<TBound> other)
            {
                throw new System.NotSupportedException();
            }
            #endregion
        }
    }
}
